package org.example.deep.utils.aes;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@Slf4j
public class AesUtil {
    /**
     * 加解密 key
     */
    private static String key = "xiatianzenmeguoa";
    private static String iv = "pijiushaokaoxlxa";

    //国网上海电力公众号号 加密相关key
    private static String shKey = "truesharemanyous";
    private static String shIv = "showbeingarebyme";
    //END

    private static String keyMini = "BraisedPorkChops";
    private static String ivMini = "MacaroonCupcakes";

    /**
     * 终端转发
     */
    private static String terminalKey = "wwwdxinforcomkey";
    private static String terminalIv = "comdxinforwwwniv";

    /**
     * 加密  转换后不含特殊字符
     *
     * @param value
     * @return
     */
    public static String encrypt(String value) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
                byte[] bytes = getBytes(cipher, value);
                String encode = new BASE64Encoder().encode(bytes);
                String str = encode.replaceAll("\\+", "-").replaceAll("/", "_");
                return str.replaceAll("\\r|\\n", "").replaceAll("=", "");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    /**
     * 国网上海电力公众号 加密方式
     *
     * @param value
     * @return
     */
    public static String encryptSH(String value) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                Cipher cipher = initCipherSH(Cipher.ENCRYPT_MODE);
                byte[] bytes = getBytes(cipher, value);
                return new BASE64Encoder().encode(bytes).replaceAll("\\r|\\n", "");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    /**
     * 解密
     *
     * @param value
     * @return
     */
    public static String decrypt(String value) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                while ((value.length()) % 4 != 0) {
                    value += "=";
                }
                Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
                String str = value.replaceAll("-", "+").replaceAll("_", "/");
                byte[] buffer = new BASE64Decoder().decodeBuffer(str);
                byte[] bytes = cipher.doFinal(buffer);
                return new String(bytes, "UTF-8").trim();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    /**
     * 国网上海电力公众号 解密方式
     *
     * @param value
     * @return
     */
    public static String decryptSH(String value) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                Cipher cipher = initCipherSH(Cipher.DECRYPT_MODE);
                byte[] buffer = new BASE64Decoder().decodeBuffer(value);
                byte[] bytes = cipher.doFinal(buffer);
                return new String(bytes, "UTF-8").trim();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    public static JSONObject encryptWxPay(String value, String add) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                JSONObject object = new JSONObject();
                String nonce = System.currentTimeMillis() + "";
                Cipher cipher = initCipherGCM("32位key", Cipher.ENCRYPT_MODE, nonce, add);
                byte[] bytes = cipher.doFinal(value.getBytes());
                String encode = new BASE64Encoder().encode(bytes);
                object.put("value", encode);
                object.put("nonce", nonce);
                object.put("add", add);
                return object;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    public static String decryptWxPay(String value, String nonce, String add) {
        if (StrUtil.isNotEmpty(value)) {
            try {
                Cipher cipher = initCipherGCM("32位key", Cipher.DECRYPT_MODE, nonce, add);
                byte[] bytes = cipher.doFinal(new BASE64Decoder().decodeBuffer(value));
                return new String(bytes, "UTF-8").trim();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    private static byte[] getBytes(Cipher cipher, String value) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        int blockSize = cipher.getBlockSize();
        byte[] dataBytes = value.getBytes("UTF-8");
        int plaintextLength = dataBytes.length;
        if (plaintextLength % blockSize != 0) {
            plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
        }
        byte[] plaintext = new byte[plaintextLength];
        System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
        return cipher.doFinal(plaintext);
    }

    private static Cipher initCipher(int mode) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
        cipher.init(mode, secretKeySpec, ivParameterSpec);
        return cipher;
    }

    private static Cipher initCipherSH(int mode) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(shKey.getBytes(), "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(shIv.getBytes());
        cipher.init(mode, secretKeySpec, ivParameterSpec);
        return cipher;
    }

    /**
     * @param key   加密key
     * @param mode  类型： 1加密，2：解密
     * @param nonce 随机串初始化向量
     * @param aad   额外数据认证
     * @return
     * @throws UnsupportedEncodingException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     */
    private static Cipher initCipherGCM(String key, int mode, String nonce, String aad) throws UnsupportedEncodingException, InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes("UTF-8"));
        cipher.init(mode, secretKeySpec, spec);
        if (StrUtil.isNotEmpty(aad)) {
            cipher.updateAAD(aad.getBytes("UTF-8"));
        }
        return cipher;
    }

    /**
     * 加密  转换后不含特殊字符
     *
     * @param value
     * @return
     */
    public static String encryptMINI(String value) {
        if (!StringUtils.isEmpty(value)) {
            try {
                Cipher cipher = initCipherMINI(Cipher.ENCRYPT_MODE);
                byte[] bytes = getBytes(cipher, value);
                String encode = new BASE64Encoder().encode(bytes);
                String str = encode.replaceAll("\\+", "-").replaceAll("/", "_");
                return str.replaceAll("\\r|\\n", "").replaceAll("=", "");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    /**
     * 解密
     *
     * @param value
     * @return
     */
    public static String decryptMINI(String value) {
        if (!StringUtils.isEmpty(value)) {
            try {
                while ((value.length()) % 4 != 0) {
                    value += "=";
                }
                Cipher cipher = initCipherMINI(Cipher.DECRYPT_MODE);
                String str = value.replaceAll("-", "+").replaceAll("_", "/");
                byte[] buffer = new BASE64Decoder().decodeBuffer(str);
                byte[] bytes = cipher.doFinal(buffer);
                return new String(bytes, "UTF-8").trim();
            } catch (Exception e) {
                log.info("Failed to decrypt: " + value);
                throw new RuntimeException(e);
            }
        }
        return "";
    }

    private static Cipher initCipherMINI(int mode) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyMini.getBytes(), "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivMini.getBytes());
        cipher.init(mode, secretKeySpec, ivParameterSpec);
        return cipher;
    }
    /**
     * 解密
     *
     * @return
     * @throws Exception
     */
    public static String desEncryptForTermianl(String data) throws Exception {
        if (StrUtil.isNotEmpty(data) && !"null".equals(data)) {
            //data = URLDecoder.decode(data,"UTF-8");
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keyspec = new SecretKeySpec(terminalKey.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(terminalIv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, "UTF-8");
            return originalString.trim();
        } else {
            return null;
        }

    }


    /**
     * 加密
     *
     * @return
     * @throws Exception
     */
    public static String encryptForTermianl(String data) throws Exception {
        if (StrUtil.isNotEmpty(data)) {
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();
            byte[] dataBytes = data.getBytes("UTF-8");
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }
            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
            SecretKeySpec keyspec = new SecretKeySpec(terminalKey.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(terminalIv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            return new BASE64Encoder().encode(encrypted);
        } else {
            return null;
        }
    }

    public static void main(String[] args) throws Exception{
        desEncryptForTermianl("YKdflAFSfOR1Rj9KeUk4cR1SaVC1/GvBkRu+iIGMN0WtxM+yJjpQY7U2VlF8JqKnwuDgDxR6S+e+LyvXWbEJ1Br7SN67q6tsjskODKtbOcBLUzFt30+1f4SoNnalFrIU");
    }
}